/* global BufferEncoding */
import * as Minio from 'minio';
import logger from '../logger';
import { Readable } from 'stream';
import { StorageConfig } from './type';
import {ObjectItem, ObjectStorage} from './objectStorage';
// @ts-ignore
import { UploadedObjectInfo } from 'minio';
import path from "path";
import fs from 'fs';


export class MinioStorage implements ObjectStorage {

  private minioClient: Minio.Client;

  constructor(config: StorageConfig) {

    console.log(JSON.stringify(config))

    this.minioClient = new Minio.Client({
      endPoint: config.host,
      port: config.port,
      useSSL: config.useSSL,
      accessKey: config.accessKey,
      secretKey: config.accessSecret
    });
  }

  putObject(bucket: string, objectName: string, content: string | Buffer): Promise<UploadedObjectInfo> {
    return this.minioClient.putObject(bucket, objectName, content);
  }
  fPutObject(bucket: string, objectName: string, filePath: string, metadata: any = {}): Promise<UploadedObjectInfo> {
    return this.minioClient.fPutObject(bucket, objectName, filePath, metadata);
  }

  fPutObjectWithProgress(bucket: string, objectName: string, filePath: string, progressCallback?: (percentage: number) => void, metadata: any = {}): Promise<UploadedObjectInfo> {
    return new Promise((resolve, reject) => {
      const fileStats = fs.statSync(filePath);
      const totalSize = fileStats.size;
      let uploadedSize = 0;

      // 创建文件读取流
      const fileStream = fs.createReadStream(filePath);
      
      // 监听数据传输进度
      fileStream.on('data', (chunk) => {
        uploadedSize += chunk.length;
        const percentage = Math.round((uploadedSize / totalSize) * 100);
        
        if (progressCallback) {
          progressCallback(percentage);
        }
      });

      // 使用流上传
      this.minioClient.putObject(bucket, objectName, fileStream, totalSize, metadata)
        .then((result) => {
          resolve(result);
        })
        .catch((error) => {
          reject(error);
        });
    });
  }
  getObject(bucket: string, objectName: string): Promise<Readable> {
    return this.minioClient.getObject(bucket, objectName);
  }

  getString(bucket: string, objectName: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
    return new Promise<string>(async (resolve, reject) => {
      let readStream: Readable;
      try {
        readStream = await this.getObject(bucket, objectName);
      } catch ( e ) {
        reject(e)
      }
      if (readStream !== undefined) {
        readStream.setEncoding(encoding);
        let content = '';
        readStream.on('data', (trunk) => {
          content += trunk;
        });
        readStream.on('error', (error) => {
          logger.error(`read object(bucket=${bucket},objectName=${objectName}) failed`);
          reject(error);
        });
        readStream.on('end', async () => {
          resolve(content);
        });
      }
    });
  }

  fGetObject(bucket: string, objectName: string, filePath: string): Promise<void> {
    return this.minioClient.fGetObject(bucket, objectName, filePath);
  }
  removeObject(bucket: string, objectName: string): Promise<void> {
    return this.minioClient.removeObject(bucket, objectName);
  }

  listObjects(bucket: string, objectName: string): Promise<ObjectItem[]> {
    return new Promise((resolve, reject) => {
      const objectList: Minio.BucketItem[] = [];
      const stream = this.minioClient.listObjectsV2(bucket, objectName, true);

      stream.on('data', (bucketItem) => { objectList.push(bucketItem); });
      stream.on('error', reject);
      stream.on('end', (err) => {
        if (err) {
          reject(err);
          return;
        }
        const convertedList = objectList.reduce((acc, cur) => {
          acc.push({
            name: path.basename(cur.name),
            value: cur.name,
            lastModified: cur.lastModified.toISOString()
          });
          return acc;
        }, [] as ObjectItem[]);

        resolve(convertedList);
      });
    });
  }

  signatureUrl(bucket: string, objectName): Promise<any> {
    return this.minioClient.presignedGetObject(bucket, objectName);
  }

}